package com.tansun.easycare.rule.datacomponent.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.transform.stream.StreamSource;

import org.dmg.pmml.Application;
import org.dmg.pmml.FieldName;
import org.dmg.pmml.PMML;
import org.jpmml.evaluator.Evaluator;
import org.jpmml.evaluator.FieldValue;
import org.jpmml.evaluator.InputField;
import org.jpmml.evaluator.ModelEvaluatorBuilder;
import org.jpmml.evaluator.TargetField;
import org.jpmml.evaluator.tree.NodeScoreDistribution;
import org.jpmml.model.JAXBUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.tansun.easycare.rule.common.service.DataBomService;
import com.tansun.easycare.rule.common.service.impl.RuleBaseServiceImpl;
import com.tansun.easycare.rule.datacomponent.model.DataMiningVo;
import com.tansun.easycare.rule.datacomponent.service.IDataMiningService;
import com.tansun.easycare.rule.referencedata.dao.RuleReferenceDataDao;
import com.tansun.easycare.rule.referencedata.entity.RuleReferenceData;
import com.tansun.easycare.rule.referencedata.service.RuleReferenceDataService;
import com.tansun.easycare.rule.rulemange.dao.RuleManageDao;
import com.tansun.easycare.rule.rulemange.entity.RuleManage;
import com.tansun.easycare.rule.rulemange.service.RuleManageService;
import com.tansun.easycare.rule.ruleversion.entity.RuleVersioninfo;
import com.tansun.easycare.rule.ruleversion.service.RuleVersioninfoService;
import com.tansun.rule.common.RuleModelTypeEnum;

@Service
public class DataMiningServiceImpl  extends RuleBaseServiceImpl<RuleManageDao, RuleManage> implements IDataMiningService{
	
	@Autowired
	private  RuleManageService  ruleManageService;
	
	@Autowired
    private  RuleVersioninfoService  ruleVersioninfoService;
	
	@Autowired
	private  RuleReferenceDataService  ruleReferenceDataService;
	
	@Autowired
	private RuleReferenceDataDao ruleReferenceDataDao;
	
    @Autowired
    private DataBomService dataBomService;
    
	@Override
	public void saveOrUpdate(DataMiningVo vo) throws Exception {
		RuleManage ruleManage =ruleManageService.getById(vo.getRuleManage().getId());
		RuleReferenceData ruleReferenceData = new RuleReferenceData(); 
		ruleReferenceData.setManageId(ruleManage.getId());
		ruleReferenceDataDao.deleteByManage(ruleReferenceData);
		ruleReferenceDataService.insertBatch(vo.getRefParams(), new String[]{"manageId","projectCode","modelType","version","code"}, new Object[]{
				ruleManage.getId(),ruleManage.getProjectCode(),ruleManage.getModelType(),ruleManage.getVersion(),ruleManage.getCode()
		});
		
		RuleVersioninfo ruleVersioninfo = new RuleVersioninfo();
		ruleVersioninfo.init(ruleManage, null);
		List<RuleVersioninfo> versionList = this.ruleVersioninfoService.findList(ruleVersioninfo);
		if(versionList==null||versionList.size()==0){
			throw new RuntimeException("数据丢失,CODE:"+ruleManage.getId());
		}
		ruleVersioninfo = versionList.get(0);
		
		this.ruleVersioninfoService.update(ruleVersioninfo);
	}

	@Override
	public void upload(MultipartFile file,String projectCode,String optType,String rankRelationId,String userId) throws Exception {
//		PMML pmml = org.jpmml.model.PMMLUtil.unmarshal(file.getInputStream());
		PMML pmml = JAXBUtil.unmarshalPMML(new StreamSource(file.getInputStream()));
		Application application = pmml.getHeader().getApplication();
		String pmmlName = application!=null?application.getName():"未命名";
		RuleManage ruleManage = null;
		RuleManage condition = new RuleManage();
		condition.setModelType(RuleModelTypeEnum.DataMining.getCode());
		condition.setProjectCode(projectCode);
		condition.setName(pmmlName);
		condition.setRankRelationId(rankRelationId);
		List<RuleManage> existList = ruleManageService.findList(condition);
		if("add".equals(optType)) {
			if(existList.size()>0) {
				throw new Exception("新增失败，已存在同名模型");
			}else {
				Date date = new Date();
				ruleManage = new RuleManage();
				ruleManage.setModelType(RuleModelTypeEnum.DataMining.getCode());
				ruleManage.setName(pmmlName);
				ruleManage.setProjectCode(projectCode);
				ruleManage.setRankRelationId(rankRelationId);
				ruleManage.setCreateDate(date);
				ruleManage.setUpdateDate(date);
				ruleManage.setCreateBy(userId);
				ruleManage.setUpdateBy(userId);
			}
		}else {
			if(existList.size()==0) {
				throw new Exception("更新失败，不存在已有模型，请选择新增操作");
			}
			ruleManage = existList.get(0);
		}
//		ruleManage.setVersion("v"+pmml.getHeader().getApplication().getVersion());
		ruleManageService.saveOrUpdate(ruleManage);
		
		RuleVersioninfo ruleVersioninfo = new RuleVersioninfo();
		ruleVersioninfo.init(ruleManage, null);
//		List<RuleVersioninfo> versionList = this.ruleVersioninfoService.findList(ruleVersioninfo);
//		if(versionList!=null&&versionList.size()>0){
//			ruleVersioninfo = versionList.get(0);
//		}
		ruleVersioninfo.setRuleContent(new String(file.getBytes(),"UTF-8"));
		this.ruleVersioninfoService.save(ruleVersioninfo);
		
		
	}

	@Override
	public DataMiningVo findDataMiningModdel(String code, String projectCode) throws Exception{
		DataMiningVo dataMiningVo = new DataMiningVo();
		RuleManage ruleManage = ruleManageService.findByCode(code, projectCode);
		dataMiningVo.setRuleManage(ruleManage);
		RuleVersioninfo ruleVersioninfo = new RuleVersioninfo();
		ruleVersioninfo.init(ruleManage, null);
		List<RuleVersioninfo> versionList = this.ruleVersioninfoService.findList(ruleVersioninfo);
		if(versionList==null||versionList.size()==0){
			throw new RuntimeException("数据丢失,CODE:"+ruleManage.getId());
		}
		ruleVersioninfo = versionList.get(0);
		InputStream is = null;
		try {
			is = new ByteArrayInputStream(ruleVersioninfo.getRuleContent().getBytes());
//			PMML pmml = org.jpmml.model.PMMLUtil.unmarshal(is);
			PMML pmml = JAXBUtil.unmarshalPMML(new StreamSource(is));
			dataMiningVo.setDesc(pmml.getHeader().getDescription());
			if(pmml.getHeader().getTimestamp().getContent().size()>0){
				dataMiningVo.setTimestamp(pmml.getHeader().getTimestamp().getContent().get(0).toString());
			}
			dataMiningVo.setModels(pmml.getModels());

			ModelEvaluatorBuilder modelEvaluatorBuilder = new ModelEvaluatorBuilder(pmml);
			Evaluator evaluator = modelEvaluatorBuilder.build();
			
			dataMiningVo.setInputFields(evaluator.getInputFields());//输入变量
			dataMiningVo.setOutputFields(evaluator.getOutputFields());//预测概率输出
			dataMiningVo.setTargetFields(evaluator.getTargetFields());//目标变量，即输出变量
		}finally{
			if(is!=null) is.close();
		}
		

		RuleReferenceData ref = new RuleReferenceData();
		ref.setManageId(ruleManage.getId());
		List<RuleReferenceData> refList = ruleReferenceDataService.findList(ref);
		dataMiningVo.setRefMap(getRefParams(refList));
		
		return dataMiningVo;
	}
	
	private Map<String,RuleReferenceData> getRefParams(List<RuleReferenceData> refList){
		Map<String,RuleReferenceData> refMap = new HashMap<String, RuleReferenceData>();
		for(RuleReferenceData ref:refList){
			if(StringUtils.isNotEmpty(ref.getRefManageId())) {
				refMap.put(ref.getRefManageId(), ref);
			}
		}
		return refMap;
	}

	@Override
	public void evaluateModel(String code, String projectCode, Map dataMap) {
		RuleManage ruleManage = ruleManageService.findByCode(code, projectCode);
		RuleVersioninfo ruleVersioninfo = new RuleVersioninfo();
		ruleVersioninfo.init(ruleManage, null);
		List<RuleVersioninfo> versionList = this.ruleVersioninfoService.findList(ruleVersioninfo);
		if(versionList==null||versionList.size()==0){
			throw new RuntimeException("数据丢失,CODE:"+ruleManage.getId());
		}
		ruleVersioninfo = versionList.get(0);
		RuleReferenceData ref = new RuleReferenceData();
		ref.setManageId(ruleManage.getId());
		List<RuleReferenceData> refList = ruleReferenceDataService.findList(ref);
		Map<String, RuleReferenceData> refParams = getRefParams(refList);
		InputStream is = null;
		try {
			is = new ByteArrayInputStream(ruleVersioninfo.getRuleContent().getBytes());
//			PMML pmml = org.jpmml.model.PMMLUtil.unmarshal(is);
			PMML pmml = JAXBUtil.unmarshalPMML(new StreamSource(is));

			ModelEvaluatorBuilder modelEvaluatorBuilder = new ModelEvaluatorBuilder(pmml);
			Evaluator evaluator = modelEvaluatorBuilder.build();
			
			//Preparing the argument map:
			Map<FieldName, FieldValue> arguments = new LinkedHashMap();

			List<InputField> inputFields = evaluator.getInputFields();
			for(InputField inputField : inputFields){
				FieldName inputFieldName = inputField.getName();

				// The raw (ie. user-supplied) value could be any Java primitive value
				RuleReferenceData ruleReferenceData = refParams.get(inputFieldName.getValue());
				Map data = (Map) dataMap.get(ruleReferenceData.getClassName());
				Object rawValue = data.get(ruleReferenceData.getNameen());

				// The raw value is passed through: 1) outlier treatment, 2) missing value treatment, 3) invalid value treatment and 4) type conversion
				FieldValue inputFieldValue = inputField.prepare(rawValue);

				arguments.put(inputFieldName, inputFieldValue);
			}
			
			Map<FieldName, ?> results = evaluator.evaluate(arguments);
			
			//Extracting secondary results from the result map:
			System.out.println("输出值---------------：");
			List<TargetField> targetFields = evaluator.getTargetFields();
			for(TargetField targetField : targetFields){
				FieldName targetFieldName = targetField.getName();

				Object targetFieldValue = null;
				Object obj = results.get(targetFieldName);
				/**
				 * ToDo:需要修改，目前针对R生成线性回归模型，python的决策树模型，需要添加其他的
				 */
				if(obj instanceof NodeScoreDistribution){
	        		NodeScoreDistribution node = (NodeScoreDistribution)obj;
	        		targetFieldValue = node.getResult();
				}else{
					targetFieldValue = obj;
				}
				RuleReferenceData ruleReferenceData = refParams.get(targetFieldName.getValue());
				Map data = (Map) dataMap.get(ruleReferenceData.getClassName());
				data.put(ruleReferenceData.getNameen(), targetFieldValue);
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			if(is!=null)
				try {
					is.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
		
	}
}
